﻿using System;
using System.Linq;
using System.Reflection;
using XenoGears.Assertions;
using XenoGears.Reflection;
using XenoGears.Reflection.Attributes;
using XenoGears.Reflection.Shortcuts;

namespace Jink.Protocol.Core.StatusCodes
{
    public static class StatusCodeHelper
    {
        public static StatusCode? AcquireStatusCode(this Exception ex)
        {
            if (ex == null)
            {
                return StatusCode.OK;
            }
            else if (ex is NotImplementedException)
            {
                return StatusCode.NotImplemented;
            }
            else
            {
                var tex = ex.GetType();
                var staticCode = tex.AttrOrNull<StatusCodeAttribute>();
                var dynamicCode = tex.GetMembers(BF.All).SingleOrDefault(m => m.HasAttr<StatusCodeProviderAttribute>());

                if (staticCode == null && dynamicCode == null)
                {
                    return null;
                }
                else if (staticCode != null && dynamicCode == null)
                {
                    return staticCode.StatusCode;
                }
                else if (staticCode == null && dynamicCode != null)
                {
                    if (dynamicCode is FieldInfo)
                    {
                        var fi = dynamicCode.AssertCast<FieldInfo>();
                        return (StatusCode)(fi.IsStatic() ? fi.GetValue(null) : fi.GetValue(ex));
                    }
                    else if (dynamicCode is PropertyInfo)
                    {
                        var pi = dynamicCode.AssertCast<PropertyInfo>();
                        return (StatusCode)(pi.IsStatic() ? pi.GetValue(null, null) : pi.GetValue(ex, null));
                    }
                    else if (dynamicCode is MethodInfo)
                    {
                        var mi = dynamicCode.AssertCast<MethodInfo>();
                        return (StatusCode)(mi.IsStatic() ? mi.Invoke(null, null) : mi.Invoke(ex, null));
                    }
                    else
                    {
                        throw AssertionHelper.Fail();
                    }
                }
                else
                {
                    throw AssertionHelper.Fail();
                }
            }
        }
    }
}
